<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>六足机器人上位机</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/three@0.155.0/build/three.module.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.155.0/examples/jsm/controls/OrbitControls.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.155.0/examples/jsm/loaders/GLTFLoader.min.js"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#36D399',
                        accent: '#FF6B35',
                        dark: '#1E293B',
                        light: '#F8FAFC',
                        frame: '#E2E8F0',
                        control: '#FF5722',
                        radar: '#00FF00',
                    },
                    fontFamily: {
                        inter: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .grid-layout {
                grid-template-rows: 65% 35%;
                height: 100%;
            }
            .grid-layout-right {
                grid-template-rows: 50% 50%;
                height: 100%;
            }
            .frame-border {
                border: 5px solid theme('colors.frame');
            }
            .app-height {
                height: calc(100vh);
            }
            .key-button {
                @apply w-12 h-12 flex items-center justify-center rounded-lg bg-gray-100 text-gray-700 font-medium transition-all duration-200 shadow-sm;
            }
            .key-button-active {
                @apply bg-control text-white transform scale-105 shadow-md;
            }
            .robot-arena {
                @apply relative w-full h-full bg-gray-100 rounded-lg overflow-hidden;
            }
            .robot {
                @apply absolute w-12 h-12 bg-control rounded-full flex items-center justify-center text-white transition-all duration-150 shadow-md;
            }
            .keyboard-container {
                @apply grid grid-cols-3 gap-2 justify-center max-w-[12rem] mx-auto;
            }
            .video-container {
                @apply relative w-full h-full bg-gray-100 rounded-lg overflow-hidden;
            }
            .video-placeholder {
                @apply absolute inset-0 flex flex-col items-center justify-center bg-gray-100;
            }
            .robot-visualization {
                @apply w-full h-full bg-gray-50 rounded-lg overflow-hidden;
            }
            .radar-container {
                @apply w-full h-full bg-dark rounded-lg overflow-hidden relative;
            }
            .radar-controls {
                @apply absolute top-2 right-2 flex gap-1 z-10;
            }
            .radar-control-btn {
                @apply bg-white/20 hover:bg-white/40 text-white p-1 rounded text-xs transition-colors;
            }
        }
    </style>
</head>
<body class="font-inter bg-gray-50">
    <!-- 主要内容区 -->
    <div class="relative app-height frame-border overflow-hidden w-full max-w-7xl mx-auto">
        <!-- 左右分隔线 -->
        <div class="absolute left-1/2 top-0 bottom-0 w-[5px] bg-frame -ml-[2.5px] z-10"></div>

        <!-- 左侧区域 -->
        <div class="absolute left-0 top-0 bottom-0 w-1/2 grid grid-layout">
            <!-- 左侧上下分隔线 -->
            <div class="absolute left-0 right-0 top-[65%] h-[5px] bg-frame -mt-[2.5px] z-10"></div>

            <!-- 左上区域 - ESP32-CAM视频显示 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-primary/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-xl font-bold text-dark mb-3 flex items-center">
                        <i class="fa fa-area-chart text-primary mr-2"></i>
                        摄像头
                    </h2>

                    <!-- 视频显示区域 -->
                    <div class="video-container">
                        <!-- 视频占位符，视频加载时显示 -->
                        <div id="video-placeholder" class="video-placeholder">
                            <div class="animate-pulse rounded-full h-16 w-16 bg-gray-300 mb-3"></div>
                            <div class="animate-pulse rounded h-3 w-3/4 bg-gray-300 mb-2"></div>
                            <div class="animate-pulse rounded h-3 w-1/2 bg-gray-300"></div>
                            <p class="mt-4 text-gray-500 text-sm" id="video-status-text">正在连接ESP32-CAM...</p>
                        </div>

                        <!-- 视频元素 -->
                        <img id="esp32-cam-video" src="https://picsum.photos/800/600" alt="ESP32-CAM视频流" class="w-full h-80 object-cover hidden">

                        <!-- 视频控制按钮 -->
                        <div class="absolute bottom-3 right-3 flex gap-2 z-20">
                            <button id="refresh-video" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors">
                                <i class="fa fa-refresh"></i>
                            </button>
                            <button id="fullscreen-video" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors">
                                <i class="fa fa-expand"></i>
                            </button>
                            <button id="exit-fullscreen" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors hidden">
                                <i class="fa fa-compress"></i>
                            </button>
                        </div>
                    </div>

                    <!-- 视频状态信息 -->
                    <div class="mt-2 flex justify-between items-center">
                        <div class="text-xs text-gray-500">
                            <span id="video-status">状态: <span id="video-status-detail">连接中...</span></span>
                        </div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -right-3 w-16 h-16 bg-primary/10 rounded-full"></div>
            </div>

            <!-- 左下区域 - 机器人控制 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-control/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center">
                        <i class="fa fa-gamepad text-control mr-2"></i>
                        机器人控制
                    </h2>

                    <!-- 步态切换按钮区域 -->
                    <div class="absolute top-4 right-4 flex gap-2 z-20">
                        <button id="gait-wave" class="px-2 py-1 rounded-md text-xs transition-all duration-200 bg-gray-200 text-gray-700 hover:bg-gray-300">
                            Wave
                        </button>
                        <button id="gait-triangle" class="px-2 py-1 rounded-md text-xs transition-all duration-200 bg-control text-white">
                            Triangle
                        </button>
                    </div>

                    <!-- 键盘布局 -->
                    <div class="keyboard-container mb-3">
                        <div class="col-span-3 flex justify-center">
                            <button id="btn-up" class="key-button text-sm">W</button>
                        </div>
                        <div class="col-span-3 grid grid-cols-3 gap-2">
                            <div class="col-start-1 flex justify-end">
                                <button id="btn-left" class="key-button text-sm">A</button>
                            </div>
                            <div class="flex justify-center">
                                <button id="btn-down" class="key-button text-sm">S</button>
                            </div>
                            <div class="flex justify-start">
                                <button id="btn-right" class="key-button text-sm">D</button>
                            </div>
                        </div>
                    </div>

                    <div class="robot-arena">
                        <div id="robot" class="robot">
                            <i class="fa fa-arrow-up"></i>
                        </div>
                    </div>

                    <div class="mt-2 text-xs text-gray-500 flex flex-wrap gap-x-4">
                        <div class="w-full sm:w-auto"><span>当前指令:</span> <span id="current-command">等待输入...</span></div>
                        <div class="w-full sm:w-auto"><span>步态模式:</span> <span id="gait-mode">Triangle</span></div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -right-3 w-12 h-12 bg-control/10 rounded-full"></div>
            </div>
        </div>

        <!-- 右侧区域 -->
        <div class="absolute right-0 top-0 bottom-0 w-1/2 grid grid-layout-right">
            <!-- 右侧上下分隔线 -->
            <div class="absolute left-0 right-0 top-1/2 h-[5px] bg-frame -mt-[2.5px] z-10"></div>

            <!-- 右上区域 - 六足机器人3D可视化 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-accent/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center">
                        <i class="fa fa-cogs text-accent mr-2"></i>
                        机器人3D可视化
                    </h2>

                    <!-- 3D可视化容器 -->
                    <div class="robot-visualization" id="robot-3d-container"></div>
                </div>
                <div class="absolute -top-3 -right-3 w-12 h-12 bg-accent/10 rounded-full"></div>
            </div>

            <!-- 右下区域 - 360雷达点云图 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-dark/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center">
                        <i class="fa fa-rss text-dark mr-2"></i>
                        360°雷达点云图
                    </h2>

                    <div class="radar-container" id="radar-container">
                        <!-- 雷达Canvas -->
                        <canvas id="radar-canvas" width="400" height="275"></canvas>

                        <!-- 雷达状态 -->
                        <div class="absolute bottom-2 left-2 text-xs text-white/80">
                            <span id="radar-status">状态: 初始状态</span>
                        </div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -left-3 w-12 h-12 bg-dark/10 rounded-full"></div>
            </div>
        </div>
    </div>

    <script>
        // 全局数据结构
        const esp32Data = {
            isConnected: false, // 新增：连接状态标记
            videoStream: null,
            radarPoints: [],
            robotStatus: {
                position: { x: 50, y: 50 },
                direction: 0,
                gait: 'triangle',
                radarAngle: -Math.PI / 2 // 初始雷达角度（向上）
            }
        };

        // DOM加载完成后初始化所有功能
        document.addEventListener('DOMContentLoaded', () => {
            // 初始化视频流
            initVideoStream();

            // 初始化机器人控制（包含按键反馈）
            initRobotControl();

            // 初始化3D可视化
            setTimeout(initRobot3DVisualization, 100); // 延迟初始化，确保容器尺寸正确

            // 初始化雷达点云图
            setTimeout(initRadar, 100); // 延迟初始化，确保容器尺寸正确

            // 监听窗口大小变化，重新初始化可视化组件
            window.addEventListener('resize', () => {
                if (scene && renderer) {
                    const container = document.getElementById('robot-3d-container');
                    const width = container.clientWidth;
                    const height = container.clientHeight;
                    camera.aspect = width / height;
                    camera.updateProjectionMatrix();
                    renderer.setSize(width, height);
                }

                if (radarCanvas) {
                    resizeRadarCanvas();
                }
            });
        });

        // ====================== 视频流处理 ======================
        // 全局变量，用于跟踪全屏状态
        let isFullscreen = false;

        // 初始化视频流
        function initVideoStream() {
            const videoElement = document.getElementById('esp32-cam-video');
            const videoPlaceholder = document.getElementById('video-placeholder');
            const refreshButton = document.getElementById('refresh-video');
            const fullscreenButton = document.getElementById('fullscreen-video');
            const exitFullscreenButton = document.getElementById('exit-fullscreen');
            const videoStatusDetail = document.getElementById('video-status-detail');
            const videoStatusText = document.getElementById('video-status-text');

            // 初始状态显示"连接中"
            videoStatusDetail.textContent = '连接中...';
            videoStatusText.textContent = '正在连接ESP32-CAM...';

            // 模拟连接过程（随机成功或失败）
            const isConnectionSuccessful = Math.random() < 0.1;

            setTimeout(() => {
                if (isConnectionSuccessful) {
                    // 连接成功
                    videoPlaceholder.classList.add('hidden');
                    videoElement.classList.remove('hidden');
                    videoStatusDetail.textContent = '连接成功';
                    videoStatusText.textContent = 'ESP32-CAM已连接';
                    esp32Data.isConnected = true;
                } else {
                    // 连接失败
                    videoStatusDetail.textContent = '连接失败';
                    videoStatusText.textContent = '连接ESP32-CAM超时，请重试';
                    videoStatusText.classList.add('text-red-500');
                    videoElement.classList.remove('hidden');
                    esp32Data.isConnected = false;
                }
            }, 3000); // 3秒超时时间，缩短以提高测试效率

            // 刷新视频
            refreshButton.addEventListener('click', () => {
                videoElement.classList.add('hidden');
                videoPlaceholder.classList.remove('hidden');
                videoStatusDetail.textContent = '重新连接...';
                videoStatusText.textContent = '正在重新连接ESP32-CAM...';
                videoStatusText.classList.remove('text-red-500');
                esp32Data.isConnected = false;

                // 重置连接状态
                setTimeout(() => {
                    const isReconnectionSuccessful = Math.random() > 0.3;
                    if (isReconnectionSuccessful) {
                        videoPlaceholder.classList.add('hidden');
                        videoElement.classList.remove('hidden');
                        videoStatusDetail.textContent = '连接成功';
                        videoStatusText.textContent = 'ESP32-CAM已连接';
                        esp32Data.isConnected = true;
                    } else {
                        videoStatusDetail.textContent = '连接失败';
                        videoStatusText.textContent = '重新连接ESP32-CAM超时，请重试';
                        videoStatusText.classList.add('text-red-500');
                        videoElement.classList.remove('hidden');
                        esp32Data.isConnected = false;
                    }
                }, 3000);
            });

            // 全屏显示视频
            fullscreenButton.addEventListener('click', () => {
                const videoContainer = document.querySelector('.video-container');
                requestFullscreen(videoContainer);
            });

            // 退出全屏
            exitFullscreenButton.addEventListener('click', () => {
                exitFullscreen();
            });

            // 监听全屏状态变化
            document.addEventListener('fullscreenchange', handleFullscreenChange);
            document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
            document.addEventListener('mozfullscreenchange', handleFullscreenChange);
            document.addEventListener('MSFullscreenChange', handleFullscreenChange);
        }

        // 跨浏览器请求全屏
        function requestFullscreen(element) {
            try {
                if (element.requestFullscreen) {
                    element.requestFullscreen();
                } else if (element.webkitRequestFullscreen) {
                    element.webkitRequestFullscreen();
                } else if (element.msRequestFullscreen) {
                    element.msRequestFullscreen();
                } else if (element.mozRequestFullScreen) {
                    element.mozRequestFullScreen();
                }
                isFullscreen = true;
                updateFullscreenButtons();
            } catch (error) {
                console.error('全屏请求失败:', error);
                alert('无法进入全屏模式: ' + error.message);
            }
        }

        // 跨浏览器退出全屏
        function exitFullscreen() {
            try {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                } else if (document.mozCancelFullScreen) {
                    document.mozCancelFullScreen();
                }
                isFullscreen = false;
                updateFullscreenButtons();
            } catch (error) {
                console.error('退出全屏失败:', error);
                alert('无法退出全屏模式: ' + error.message);
            }
        }

        // 处理全屏状态变化
        function handleFullscreenChange() {
            // 检查实际全屏状态
            const isActuallyFullscreen =
                document.fullscreenElement ||
                document.webkitFullscreenElement ||
                document.mozFullScreenElement ||
                document.msFullscreenElement;

            isFullscreen = isActuallyFullscreen;
            updateFullscreenButtons();
        }

        // 更新全屏按钮显示状态
        function updateFullscreenButtons() {
            const fullscreenButton = document.getElementById('fullscreen-video');
            const exitFullscreenButton = document.getElementById('exit-fullscreen');

            if (isFullscreen) {
                fullscreenButton.classList.add('hidden');
                exitFullscreenButton.classList.remove('hidden');
            } else {
                fullscreenButton.classList.remove('hidden');
                exitFullscreenButton.classList.add('hidden');
            }
        }

        // ====================== 机器人控制 ======================
        function initRobotControl() {
            const robot = {
                element: document.getElementById('robot'),
                commandDisplay: document.getElementById('current-command'),
                gaitDisplay: document.getElementById('gait-mode'),
                updateDisplay() {
                    this.element.style.left = `${esp32Data.robotStatus.position.x}%`;
                    this.element.style.top = `${esp32Data.robotStatus.position.y}%`;
                    this.element.style.transform = `rotate(${esp32Data.robotStatus.direction * 90}deg)`;
                    this.gaitDisplay.textContent = esp32Data.robotStatus.gait.charAt(0).toUpperCase() + esp32Data.robotStatus.gait.slice(1);
                }
            };

            // 按键映射
            const keyMappings = {
                'w': 'btn-up',
                's': 'btn-down',
                'a': 'btn-left',
                'd': 'btn-right'
            };

            // 移动机器人
            function moveRobot(direction) {
                const speed = 5;
                const arena = document.querySelector('.robot-arena');
                const arenaRect = arena.getBoundingClientRect();
                const robotSize = robot.element.offsetWidth;
                const maxX = 100 - (robotSize / arenaRect.width * 100);
                const maxY = 100 - (robotSize / arenaRect.height * 100);

                const prevX = esp32Data.robotStatus.position.x;
                const prevY = esp32Data.robotStatus.position.y;

                switch(direction) {
                    case 'up':
                        esp32Data.robotStatus.position.y = Math.max(0, prevY - speed);
                        esp32Data.robotStatus.direction = 0;
                        break;
                    case 'down':
                        esp32Data.robotStatus.position.y = Math.min(maxY, prevY + speed);
                        esp32Data.robotStatus.direction = 2;
                        break;
                    case 'left':
                        esp32Data.robotStatus.position.x = Math.max(0, prevX - speed);
                        esp32Data.robotStatus.direction = 3;
                        break;
                    case 'right':
                        esp32Data.robotStatus.position.x = Math.min(maxX, prevX + speed);
                        esp32Data.robotStatus.direction = 1;
                        break;
                }

                // 更新显示
                robot.updateDisplay();

                // 显示当前指令
                robot.commandDisplay.textContent = direction;

                // 更新3D模型姿态
                updateRobot3DPosition(
                    esp32Data.robotStatus.position.x,
                    esp32Data.robotStatus.position.y,
                    esp32Data.robotStatus.direction
                );
            }

            // 步态切换
            function switchGait(gaitType) {
                const gaitButtons = document.querySelectorAll('#gait-wave, #gait-triangle');
                gaitButtons.forEach(button => {
                    button.classList.remove('bg-control', 'text-white');
                    button.classList.add('bg-gray-200', 'text-gray-700');
                });

                const activeButton = document.getElementById(`gait-${gaitType}`);
                if (activeButton) {
                    activeButton.classList.remove('bg-gray-200', 'text-gray-700');
                    activeButton.classList.add('bg-control', 'text-white');
                }

                esp32Data.robotStatus.gait = gaitType;
                robot.updateDisplay();

                // 更新3D模型步态
                updateRobot3DGait(gaitType);
            }

            // 高亮按键
            function highlightKey(key) {
                const btnId = keyMappings[key.toLowerCase()];
                if (btnId) {
                    const btn = document.getElementById(btnId);
                    btn.classList.add('key-button-active');
                }
            }

            // 取消高亮按键
            function unhighlightKey(key) {
                const btnId = keyMappings[key.toLowerCase()];
                if (btnId) {
                    const btn = document.getElementById(btnId);
                    btn.classList.remove('key-button-active');
                }
            }

            // 绑定按键事件
            document.getElementById('btn-up').addEventListener('mousedown', () => {
                highlightKey('w');
                moveRobot('up');
            });
            document.getElementById('btn-down').addEventListener('mousedown', () => {
                highlightKey('s');
                moveRobot('down');
            });
            document.getElementById('btn-left').addEventListener('mousedown', () => {
                highlightKey('a');
                moveRobot('left');
            });
            document.getElementById('btn-right').addEventListener('mousedown', () => {
                highlightKey('d');
                moveRobot('right');
            });

            // 鼠标释放事件
            document.addEventListener('mouseup', () => {
                Object.keys(keyMappings).forEach(key => unhighlightKey(key));
            });

            // 键盘控制
            document.addEventListener('keydown', (e) => {
                const key = e.key.toLowerCase();
                if (keyMappings[key]) {
                    e.preventDefault(); // 防止页面滚动
                    highlightKey(key);
                    moveRobot(key === 'w' ? 'up' : key === 's' ? 'down' : key === 'a' ? 'left' : 'right');
                }
            });

            // 键盘释放事件
            document.addEventListener('keyup', (e) => {
                const key = e.key.toLowerCase();
                if (keyMappings[key]) {
                    unhighlightKey(key);
                }
            });

            // 绑定步态切换事件
            document.getElementById('gait-wave').addEventListener('click', () => switchGait('wave'));
            document.getElementById('gait-triangle').addEventListener('click', () => switchGait('triangle'));

            // 初始化机器人显示
            robot.updateDisplay();
        }

        // ====================== 3D可视化逻辑 ======================
        // ====================== 3D可视化模块 (完整重构版) ======================
        class Robot3DViewer {
            constructor() {
                this.container = document.getElementById('robot-3d-container');
                this.scene = null;
                this.camera = null;
                this.renderer = null;
                this.controls = null;
                this.robotModel = null;
                this.animationId = null;
                this.isInitialized = false;
                this.errorCount = 0; // 错误计数器

                // 配置
                this.config = {
                    useGLTFModel: true,
                    gltfUrl: 'https://cdn.staticfile.org/three.js/r128/examples/models/gltf/RobotExpressive/RobotExpressive.glb',
                    modelScale: 0.8,
                    cameraPosition: { x: 0, y: 12, z: 24 },
                    ambientLight: 0.85,
                    directionalLight: 1.2,
                    enableShadows: true
                };
            }

            // 初始化3D场景
            async init() {
                if (!this.container || this.isInitialized) return;

                // 显示加载状态
                this.showLoadingState();

                try {
                    // 1. 检查Three.js核心库是否可用
                    if (!window.THREE) {
                        await this.loadThreeCore();
                    }

                    // 2. 加载扩展模块
                    await this.loadRequiredExtensions();

                    // 3. 创建基础场景
                    this.createBaseScene();

                    // 4. 创建光源系统
                    this.setupLights();

                    // 5. 添加辅助工具
                    this.createHelpers();

                    // 6. 加载机器人模型
                    await this.loadRobotModel();

                    // 7. 初始化轨道控制器
                    this.initControls();

                    // 8. 启动动画循环
                    this.startAnimationLoop();

                    // 9. 监听窗口变化
                    this.addResizeListener();

                    this.isInitialized = true;
                    this.errorCount = 0;

                } catch (error) {
                    console.error('3D场景初始化失败:', error);
                    this.errorCount++;

                    // 提供重试建议
                    if (this.errorCount <= 2) {
                        this.showRetryErrorState(error.message);
                    } else {
                        this.showErrorState(error.message);
                    }
                }
            }

            // 创建基础场景
            createBaseScene() {
                // 创建场景
                this.scene = new THREE.Scene();
                this.scene.background = new THREE.Color(0xf8fafc);

                // 创建相机
                const aspect = this.container.clientWidth / this.container.clientHeight;
                this.camera = new THREE.PerspectiveCamera(
                    45, // 视野角度
                    aspect, // 宽高比
                    0.1, // 近裁剪面
                    1000 // 远裁剪面
                );
                this.camera.position.set(
                    this.config.cameraPosition.x,
                    this.config.cameraPosition.y,
                    this.config.cameraPosition.z
                );
                this.camera.lookAt(0, 0, 0);

                // 创建渲染器
                this.renderer = new THREE.WebGLRenderer({
                    antialias: true,
                    alpha: true,
                    powerPreference: 'high-performance'
                });
                this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
                this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
                if (this.config.enableShadows) {
                    this.renderer.shadowMap.enabled = true;
                }

                // 清空容器并添加渲染器DOM元素
                this.container.innerHTML = '';
                this.container.appendChild(this.renderer.domElement);
            }

            // 设置光源
            setupLights() {
                // 环境光
                const ambientLight = new THREE.AmbientLight(
                    0xffffff, // 颜色
                    this.config.ambientLight // 强度
                );
                this.scene.add(ambientLight);

                // 定向光源
                const directionalLight = new THREE.DirectionalLight(
                    0xffffff, // 颜色
                    this.config.directionalLight // 强度
                );
                directionalLight.position.set(5, 15, 10);
                directionalLight.castShadow = this.config.enableShadows;
                this.scene.add(directionalLight);
            }

            // 创建辅助工具
            createHelpers() {
                // 网格辅助
                const gridHelper = new THREE.GridHelper(30, 30, 0xe2e8f0, 0xcbd5e1);
                this.scene.add(gridHelper);

                // 坐标轴辅助
                const axesHelper = new THREE.AxesHelper(10);
                this.scene.add(axesHelper);
            }

            // 初始化轨道控制器
            initControls() {
                if (!window.OrbitControls) {
                    console.warn('OrbitControls未加载，取消控制器初始化');
                    return;
                }

                try {
                    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
                    this.controls.enableDamping = true;
                    this.controls.dampingFactor = 0.05;
                    this.controls.screenSpacePanning = false;
                    this.controls.maxPolarAngle = Math.PI / 2;
                } catch (error) {
                    console.error('控制器初始化失败:', error);
                }
            }

            // 加载核心库
            async loadThreeCore() {
                return new Promise((resolve, reject) => {
                    if (window.THREE) return resolve();

                    const script = document.createElement('script');
                    script.src = 'https://cdn.staticfile.org/three.js/0.155.0/three.min.js';
                    script.crossOrigin = 'anonymous';

                    script.onload = resolve;
                    script.onerror = () => reject(new Error('无法加载Three.js核心库'));

                    document.head.appendChild(script);
                });
            }

            // 加载必要扩展
            async loadRequiredExtensions() {
                const required = [
                    {
                        name: 'OrbitControls',
                        url: 'https://cdn.staticfile.org/three.js/0.155.0/examples/jsm/controls/OrbitControls.min.js',
                        test: () => window.OrbitControls
                    },
                    {
                        name: 'GLTFLoader',
                        url: 'https://cdn.staticfile.org/three.js/0.155.0/examples/jsm/loaders/GLTFLoader.min.js',
                        test: () => window.GLTFLoader
                    }
                ];

                let totalLoaded = 0;

                // 检查是否已加载
                const needLoad = required.filter(lib => !lib.test());

                if (needLoad.length === 0) return;

                for (const lib of needLoad) {
                    try {
                        await new Promise((resolve, reject) => {
                            const script = document.createElement('script');
                            script.src = lib.url;
                            script.crossOrigin = 'anonymous';

                            script.onload = () => {
                                if (lib.test()) {
                                    console.log(`${lib.name}加载成功`);
                                    resolve();
                                } else {
                                    reject(new Error(`${lib.name}未定义`));
                                }
                            };

                            script.onerror = () => reject(new Error(`加载失败: ${lib.url}`));

                            document.head.appendChild(script);
                        });
                        totalLoaded++;
                    } catch (error) {
                        console.error(`加载${lib.name}失败:`, error.message);
                        throw error;
                    }
                }
            }

            // 加载机器人模型
            async loadRobotModel() {
                // 使用临时模型作为占位
                if (!this.robotModel) {
                    this.createTemporaryRobot();
                }

                // 如果配置了GLTF模型且加载器可用
                if (this.config.useGLTFModel && window.GLTFLoader) {
                    try {
                        await this.loadGLTFModel();
                    } catch (error) {
                        console.error('GLTF模型加载失败，使用基础模型:', error);
                    }
                }
            }

            // 创建临时机器人模型
            createTemporaryRobot() {
                if (this.robotModel) return;

                const bodyGeometry = new THREE.BoxGeometry(6, 2, 3);
                const bodyMaterial = new THREE.MeshStandardMaterial({
                    color: 0x1e88e5,
                    roughness: 0.3,
                    metalness: 0.7
                });

                const body = new THREE.Mesh(bodyGeometry, bodyMaterial);

                // 创建腿部（6条）
                const legGeometry = new THREE.CylinderGeometry(0.4, 0.4, 4, 8);
                const positions = [
                    [-3, -2, 2], [-3, -2, 0], [-3, -2, -2], // 左侧
                    [3, -2, 2], [3, -2, 0], [3, -2, -2]    // 右侧
                ];

                positions.forEach(pos => {
                    const leg = new THREE.Mesh(legGeometry, bodyMaterial.clone());
                    leg.position.set(pos[0], pos[1], pos[2]);
                    leg.rotation.x = Math.PI/2;
                    body.add(leg);
                });

                body.position.y = 5;
                this.robotModel = body;
                this.scene.add(this.robotModel);
            }

            // 加载GLTF模型
            loadGLTFModel() {
                return new Promise((resolve, reject) => {
                    if (!window.GLTFLoader) {
                        reject(new Error('GLTFLoader未定义'));
                        return;
                    }

                    const loader = new GLTFLoader();

                    // 创建进度显示
                    const progressBar = this.createProgressBar();
                    this.container.appendChild(progressBar);

                    // 加载模型
                    loader.load(
                        this.config.gltfUrl,
                        (gltf) => {
                            // 移除进度条
                            progressBar.remove();

                            // 移除临时模型
                            if (this.robotModel) {
                                this.scene.remove(this.robotModel);
                            }

                            // 设置新模型
                            this.robotModel = gltf.scene;
                            this.robotModel.scale.set(
                                this.config.modelScale,
                                this.config.modelScale,
                                this.config.modelScale
                            );
                            this.robotModel.position.y = 5;
                            this.scene.add(this.robotModel);

                            resolve();
                        },
                        (xhr) => {
                            // 更新进度条
                            const percent = Math.floor(xhr.loaded / xhr.total * 100);
                            const progressFill = progressBar.querySelector('.progress-fill');
                            if (progressFill) {
                                progressFill.style.width = `${percent}%`;
                            }
                        },
                        (error) => {
                            // 移除进度条
                            progressBar.remove();
                            reject(new Error(`模型加载失败: ${error.message || '网络错误'}`));
                        }
                    );
                });
            }

            // 创建进度条
            createProgressBar() {
                const progressBar = document.createElement('div');
                progressBar.style.cssText = `
                    position: absolute;
                    top: 10px;
                    left: 50%;
                    transform: translateX(-50%);
                    width: 80%;
                    height: 8px;
                    background: rgba(200, 200, 200, 0.3);
                    border-radius: 4px;
                    overflow: hidden;
                    z-index: 10;
                `;

                const progressFill = document.createElement('div');
                progressFill.className = 'progress-fill';
                progressFill.style.cssText = `
                    position: absolute;
                    height: 100%;
                    width: 0;
                    background: linear-gradient(to right, #4ade80, #22d3ee);
                    transition: width 0.3s ease;
                `;
                progressBar.appendChild(progressFill);

                return progressBar;
            }

            // 动画循环
            startAnimationLoop() {
                const animate = () => {
                    this.animationId = requestAnimationFrame(animate);

                    // 更新控制器
                    if (this.controls && this.controls.update) {
                        this.controls.update();
                    }

                    // 渲染场景
                    this.renderer.render(this.scene, this.camera);
                };

                animate();
            }

            // 窗口大小调整处理
            addResizeListener() {
                this.onResize = () => {
                    if (!this.camera || !this.renderer) return;

                    const container = this.container;
                    this.camera.aspect = container.clientWidth / container.clientHeight;
                    this.camera.updateProjectionMatrix();
                    this.renderer.setSize(container.clientWidth, container.clientHeight);
                };

                window.addEventListener('resize', this.onResize);
            }

            // 显示加载状态
            showLoadingState() {
                this.container.innerHTML = `
                    <div class="w-full h-full flex flex-col items-center justify-center">
                        <div class="relative w-16 h-16 mb-4">
                            <div class="absolute inset-0 rounded-full border-4 border-t-blue-500 border-r-transparent border-b-transparent border-l-transparent animate-spin"></div>
                            <div class="absolute inset-0 flex items-center justify-center">
                                <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
                                </svg>
                            </div>
                        </div>
                        <div class="text-sm text-gray-600 text-center">
                          正在初始化3D场景<br>
                          <span class="text-xs mt-2">加载Three.js库和机器人模型...</span>
                        </div>
                    </div>
                `;
            }

            // 显示错误状态
            showErrorState(errorMessage) {
                this.container.innerHTML = `
                    <div class="w-full h-full flex flex-col items-center justify-center bg-red-50 p-6">
                        <div class="text-red-500 text-5xl mb-3">
                            <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                            </svg>
                        </div>
                        <h3 class="text-xl font-bold text-red-700 mt-2">3D可视化初始化失败</h3>
                        <div class="bg-red-100 rounded-md px-4 py-2 mt-3 max-w-md">
                            <p class="text-red-600 text-sm">${errorMessage || '未知错误'}</p>
                        </div>
                        <div class="mt-6 grid grid-cols-2 gap-3 w-full max-w-xs">
                            <button
                                onclick="location.reload()"
                                class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"
                            >
                                刷新页面
                            </button>
                            <button
                                onclick="robotViewer.toggleMode()"
                                class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors"
                            >
                                切换模式
                            </button>
                        </div>
                        <div class="mt-4 text-xs text-gray-500 text-center">
                            提示: 请确认浏览器是否支持WebGL 2.0<br>或尝试禁用广告拦截器
                        </div>
                    </div>
                `;
            }

            // 显示重试错误状态
            showRetryErrorState(errorMessage) {
                this.container.innerHTML = `
                    <div class="w-full h-full flex flex-col items-center justify-center bg-red-50 p-6">
                        <div class="text-red-500 text-5xl mb-3">
                            <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                            </svg>
                        </div>
                        <h3 class="text-xl font-bold text-red-700 mt-2">3D可视化初始化失败</h3>
                        <div class="bg-red-100 rounded-md px-4 py-2 mt-3 max-w-md">
                            <p class="text-red-600 text-sm">${errorMessage || '未知错误'}</p>
                        </div>
                        <button
                            id="retry-init-btn"
                            class="mt-6 px-6 py-3 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"
                        >
                            <i class="fa fa-refresh mr-2"></i>重试初始化
                        </button>
                        <div class="mt-4 text-xs text-gray-500 text-center">
                            尝试次数: ${this.errorCount}/3
                        </div>
                    </div>
                `;

                document.getElementById('retry-init-btn').addEventListener('click', () => {
                    this.init();
                });
            }

            // 切换模型模式
            toggleMode() {
                this.config.useGLTFModel = !this.config.useGLTFModel;
                this.container.innerHTML = '';

                if (this.scene) {
                    while(this.scene.children.length > 0) {
                        this.scene.remove(this.scene.children[0]);
                    }
                    this.scene = null;
                }

                if (this.renderer) {
                    this.renderer.dispose();
                    this.renderer = null;
                }

                if (this.controls) {
                    this.controls.dispose();
                    this.controls = null;
                }

                this.robotModel = null;
                this.isInitialized = false;

                // 重新初始化
                this.init();
            }

            // 清理资源
            dispose() {
                if (this.animationId) {
                    cancelAnimationFrame(this.animationId);
                }

                if (this.renderer) {
                    this.renderer.dispose();
                }

                if (this.scene) {
                    while(this.scene.children.length > 0) {
                        this.scene.remove(this.scene.children[0]);
                    }
                }

                window.removeEventListener('resize', this.onResize);

                this.container.innerHTML = '';
                this.isInitialized = false;
            }
        }

        // ====================== 全局初始化逻辑 ======================
        const robotViewer = new Robot3DViewer();

        // DOM加载完成后初始化
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(() => {
                robotViewer.init();
            }, 300);
        });

        // 更新机器人3D模型位置 (与机器人控制部分集成)
        function updateRobot3DPosition(x, y, direction) {
            if (!robotViewer.robotModel) {
                console.warn('机器人模型未加载，忽略位置更新');
                return;
            }

            // 转换为3D坐标
            const posX = (x - 50) / 5;
            const posZ = (y - 50) / 5;

            robotViewer.robotModel.position.x = posX;
            robotViewer.robotModel.position.z = posZ;
            robotViewer.robotModel.rotation.y = direction * Math.PI / 2;

            console.log(`更新机器人位置: (${x}, ${y}), 朝向: ${direction}`);
        }


        // ====================== 雷达点云图逻辑 ======================
        let radarCanvas, radarCtx;
        let radarRotation = 0;
        let radarZoom = 1;
        let radarCenterX = 200;
        let radarCenterY = 150;
        let radarRunning = true; // 控制雷达是否运行（扫描动画）

        // 初始化雷达
        function initRadar() {
            radarCanvas = document.getElementById('radar-canvas');
            radarCtx = radarCanvas.getContext('2d');

            // 调整Canvas大小以匹配容器
            resizeRadarCanvas();
            window.addEventListener('resize', resizeRadarCanvas);

            // 启动雷达动画
            document.getElementById('radar-status').textContent = '状态: 扫描中...';
            animateRadar();
        }

        // 调整雷达Canvas大小
        function resizeRadarCanvas() {
            const container = document.getElementById('radar-container');
            radarCanvas.width = container.clientWidth;
            radarCanvas.height = container.clientHeight;

            // 更新雷达中心点
            radarCenterX = radarCanvas.width / 2;
            radarCenterY = radarCanvas.height / 2;

            // 重新绘制雷达
            drawRadar();
        }

        // 切换雷达运行状态
        function toggleRadar() {
            radarRunning = !radarRunning;
            const btn = document.getElementById('toggle-radar');
            btn.innerHTML = radarRunning ? '<i class="fa fa-pause"></i>' : '<i class="fa fa-play"></i>';
            document.getElementById('radar-status').textContent = `状态: ${radarRunning ? '扫描中...' : '已暂停'}`;

            // 无论状态如何都重绘一次
            drawRadar();
        }

        // 统一绘制函数
        function drawRadar() {
            // 清除画布
            radarCtx.clearRect(0, 0, radarCanvas.width, radarCanvas.height);

            // 绘制背景
            drawRadarBackground();

            // 绘制所有点（在静态版本中为空）
            drawRadarPoints();

            // 绘制雷达扫描线（动态或静态）
            if (radarRunning) {
                drawRadarScanLine();
            } else {
                drawStaticRadarScanLine();
            }
        }

        // 雷达动画循环
        function animateRadar() {
            if (radarRunning) {
                // 清除画布
                radarCtx.clearRect(0, 0, radarCanvas.width, radarCanvas.height);

                // 绘制背景
                drawRadarBackground();

                // 绘制所有点（在静态版本中为空）
                drawRadarPoints();

                // 绘制雷达扫描线
                drawRadarScanLine();

                // 更新雷达旋转角度
                radarRotation += 0.03;
                if (radarRotation > Math.PI * 2) {
                    radarRotation -= Math.PI * 2;
                }

                requestAnimationFrame(animateRadar);
            }
        }

        // 绘制雷达背景
        function drawRadarBackground() {
            // 绘制背景黑色
            radarCtx.fillStyle = '#000';
            radarCtx.fillRect(0, 0, radarCanvas.width, radarCanvas.height);

            // 绘制雷达圆圈
            radarCtx.strokeStyle = 'rgb(255,255,255)';
            radarCtx.lineWidth = 2;

            // 绘制多个同心圆
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height) * 0.45;
            for (let i = 1; i <= 5; i++) {
                const radius = (maxRadius / 5) * i;
                radarCtx.beginPath();
                radarCtx.arc(radarCenterX, radarCenterY, radius / radarZoom, 0, Math.PI * 2);
                radarCtx.stroke();
            }

            // 绘制十字线
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX - maxRadius / radarZoom, radarCenterY);
            radarCtx.lineTo(radarCenterX + maxRadius / radarZoom, radarCenterY);
            radarCtx.moveTo(radarCenterX, radarCenterY - maxRadius / radarZoom);
            radarCtx.lineTo(radarCenterX, radarCenterY + maxRadius / radarZoom);
            radarCtx.stroke();

            // 绘制距离标记
            radarCtx.fillStyle = 'rgb(255,255,255)';
            radarCtx.font = '10px monospace';

            for (let i = 1; i <= 5; i++) {
                const radius = (maxRadius / 5) * i;
                const text = `${i * 50}`;
                const textWidth = radarCtx.measureText(text).width;

                radarCtx.fillText(text, radarCenterX + radius / radarZoom - textWidth - 5, radarCenterY - 5);
            }
        }

        // 绘制雷达点（在静态版本中为空）
        function drawRadarPoints() {
            // 静态版本不绘制任何点（无障碍环境）
        }

        // 绘制静态雷达扫描线
        function drawStaticRadarScanLine() {
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height) * 0.45;
            const staticAngle = Math.PI / 4; // 固定扫描线角度

            // 绘制扫描线
            radarCtx.strokeStyle = 'rgba(0, 255, 0, 0.3)';
            radarCtx.lineWidth = 2;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.lineTo(
                radarCenterX + Math.cos(staticAngle) * maxRadius / radarZoom,
                radarCenterY + Math.sin(staticAngle) * maxRadius / radarZoom
            );
            radarCtx.stroke();

            // 绘制扇形扫描区域
            const gradient = radarCtx.createRadialGradient(
                radarCenterX, radarCenterY, 0,
                radarCenterX, radarCenterY, maxRadius / radarZoom
            );

            gradient.addColorStop(0, 'rgba(0, 255, 0, 0.05)');
            gradient.addColorStop(1, 'rgba(0, 255, 0, 0)');

            radarCtx.fillStyle = gradient;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.arc(radarCenterX, radarCenterY, maxRadius / radarZoom, staticAngle - 0.3, staticAngle);
            radarCtx.closePath();
            radarCtx.fill();
        }

        // 绘制动态雷达扫描线
        function drawRadarScanLine() {
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height) * 0.45;

            // 绘制扫描线
            radarCtx.strokeStyle = 'rgb(255,255,255)';
            radarCtx.lineWidth = 2;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.lineTo(
                radarCenterX + Math.cos(radarRotation) * maxRadius / radarZoom,
                radarCenterY + Math.sin(radarRotation) * maxRadius / radarZoom
            );
            radarCtx.stroke();

            // 绘制扇形扫描区域
            const gradient = radarCtx.createRadialGradient(
                radarCenterX, radarCenterY, 0,
                radarCenterX, radarCenterY, maxRadius / radarZoom
            );

            gradient.addColorStop(0, 'rgba(0, 255, 0, 0.05)');
            gradient.addColorStop(1, 'rgba(0, 255, 0, 0)');

            radarCtx.fillStyle = gradient;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.arc(radarCenterX, radarCenterY, maxRadius / radarZoom, radarRotation - 0.3, radarRotation);
            radarCtx.closePath();
            radarCtx.fill();
        }
    </script>
</body>
</html>
